package cn.itcast.protocol;

import cn.itcast.config.Config;
import cn.itcast.message.Message;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageCodec;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

/**
 * 必须和 LengthFieldBasedFrameDecoder 一起使用，确保接到的 ByteBuf 消息是完整的
 */
@Slf4j
@ChannelHandler.Sharable
public class MessageCodecSharable extends MessageToMessageCodec<ByteBuf, Message> {

    /**
     * 编码处理：
     * <p>
     * maxFrameLength=最大帧长度，防止过大的帧导致内存溢出 1024
     * lengthFieldOffset=长度字段在消息中的偏移量。 12
     * lengthFieldLength=长度字段本身的长度（以字节为单位），例如 4 表示使用两个字节表示长度。
     * lengthAdjustment=长度字段所表示的值与实际内容长度之间的差异。 0
     * initialBytesToStrip=解码时从帧中剥离的字节数，默认情况下是剥离掉整个长度字段。 0
     * <p>
     * 消息格式：
     * |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15...
     * 0~3：魔数（4字节）
     * 4：版本号（1字节）
     * 5：序列化方式（1字节）
     * 6：消息类型（1字节）
     * 7~10：请求序列号（4字节）
     * 11：无意义（1字节）
     * 12... ：内容（n字节）
     *
     * @param ctx     ctx
     * @param msg     msg
     * @param outList outList
     * @throws Exception Exception
     */
    @Override
    public void encode(ChannelHandlerContext ctx, Message msg, List<Object> outList) throws Exception {
        ByteBuf out = ctx.alloc().buffer();
        // 1. 4 字节的魔数
        out.writeBytes(new byte[]{1, 2, 3, 4});
        // 2. 1 字节的版本,
        out.writeByte(1);
        // 3. 1 字节的序列化方式 jdk 0 , json 1
        out.writeByte(Config.getSerializerAlgorithm().ordinal());
        // 4. 1 字节的指令类型
        out.writeByte(msg.getMessageType());
        // 5. 4 个字节
        out.writeInt(msg.getSequenceId());
        // 无意义，对齐填充
        out.writeByte(0xff);

        //--------------------------------------- 前面已经占用12字节

        // 6. 获取内容的字节数组
        //6.1 直接写死
//        ByteArrayOutputStream bos = new ByteArrayOutputStream();
//        ObjectOutputStream oos = new ObjectOutputStream(bos);
//        oos.writeObject(msg);
//        byte[] bytes = bos.toByteArray();

        //6.2 使用java算法-写死
//        byte[] bytes = Serializer.Algorithm.Java.serialize(msg);

        //6.3 选取算法-计算消息体的长度
        byte[] bytes = Config.getSerializerAlgorithm().serialize(msg);


        // 7. 长度
        out.writeInt(bytes.length); //长度字段占用4个字节
        // 8. 写入内容
        out.writeBytes(bytes); //最后写入内容
        outList.add(out);
    }


    /**
     * 解码处理：
     * |0|1|2|3|4|5|6|7|8|9|10|11|12|13|14|15...
     * 0~3：魔数（4字节）
     * 4：版本号（1字节）
     * 5：序列化方式（1字节）
     * 6：消息类型（1字节）
     * 7~10：请求序列号（4字节）
     * 11：无意义（1字节）
     * 12... ：内容（n字节）
     *
     *
     * @param ctx ctx
     * @param in in
     * @param out  out
     * @throws Exception Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        int magicNum = in.readInt(); //魔数
        byte version = in.readByte(); //版本
        byte serializerAlgorithm = in.readByte(); //序列化方式/算法
        byte messageType = in.readByte(); //消息类型 0 1 2 ......
        int sequenceId = in.readInt(); //序列号
        in.readByte(); //对齐填充
        //--------------------------------------- 已经读取前12字节的数据
        int length = in.readInt(); //读取4字节，获取内容长度
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length); //读取内容

        //6.1 直接写死
//        ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
//        Message message = (Message) ois.readObject();


        //6.2 使用java算法-写死
//        Message message = Serializer.Algorithm.Java.deserialize(Message.class, bytes);

        //6.3 获取算法
        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlgorithm];
        //6.4 确定具体消息类型0 1 2 ....
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        //6.5 使用算法和消息类型进行反序列化
        Message message = algorithm.deserialize(messageClass, bytes);


        log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerAlgorithm, messageType, sequenceId, length);
        log.debug("{}", message);
        //7. 放入out
        out.add(message);
    }

    private static void extracted(ByteBuf in, List<Object> out) {
        int magicNum = in.readInt();
        byte version = in.readByte();
        byte serializerAlgorithm = in.readByte(); // 0 或 1
        byte messageType = in.readByte(); // 0,1,2...
        int sequenceId = in.readInt();
        in.readByte();
        int length = in.readInt();
        byte[] bytes = new byte[length];
        in.readBytes(bytes, 0, length);

        // 找到反序列化算法
        Serializer.Algorithm algorithm = Serializer.Algorithm.values()[serializerAlgorithm];
        // 确定具体消息类型
        Class<? extends Message> messageClass = Message.getMessageClass(messageType);
        Message message = algorithm.deserialize(messageClass, bytes);
//        log.debug("{}, {}, {}, {}, {}, {}", magicNum, version, serializerType, messageType, sequenceId, length);
//        log.debug("{}", message);
        out.add(message);
    }

}
